import tkinter as tk
import os
import json
import smtplib
import threading
import tempfile
import time
import py7zr
import multivolumefile
from tkinter import filedialog, ttk, messagebox
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders
from tkinterdnd2 import DND_FILES, TkinterDnD

class sendtoABCmail():
    
    def __init__(self):
        self.root = TkinterDnD.Tk()
        self.root.withdraw()    # 隐藏窗口
        self.root.title("SendToEmail")
        self.root.geometry("300x380")
        self.root.resizable(False, False)
        
        # 设置窗口图标
        try:
            self.root.iconbitmap("logo.ico")
        except Exception as e:
            print(f"设置图标失败: {e}")

        # 初始化选中的文件路径
        self.selected_files = []  # 改为列表以保持一致性

        # 创建标签页
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        self.create_send_tab()
        self.create_settings_tab()

        # 创建状态栏
        self.create_status_bar()

        # 加载已保存的设置
        self.load_settings()
            
        self.center_window()
        self.root.deiconify()   # 显示窗口
    def create_send_tab(self):
        """创建发送邮件标签页"""
        self.send_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.send_frame, text="发送邮件")
        
        # 收件箱框架
        input_frame = tk.LabelFrame(self.send_frame, padx=10, pady=10)
        input_frame.pack(fill="x", padx=10, pady=10)

        # 接收邮箱输入框
        email_frame = tk.Frame(input_frame)
        email_frame.pack(padx=5, fill="x")
        tk.Label(email_frame, text="接收邮箱:").pack(side="left")
        self.email_entry = tk.Entry(email_frame, width=30)
        self.email_entry.pack(side="right", expand=True, fill="x")
        
        # 分卷大小输入框
        volume_frame = tk.Frame(input_frame)
        volume_frame.pack(padx=5, pady=(5,0),fill="x")
        tk.Label(volume_frame, text="分卷大小:").pack(side="left")
        self.volume_size_entry = tk.Entry(volume_frame, width=20)
        self.volume_size_entry.pack(side="left")
        self.volume_size_entry.insert(0, "20")  # 设置默认值为20
        tk.Label(volume_frame, text="MB").pack(side="right")
        
        # 解压密码输入框
        password_frame = tk.Frame(input_frame)
        password_frame.pack(pady=(5,0), padx=5, fill="x")
        tk.Label(password_frame, text="解压密码:").pack(side="left")
        self.extract_password_entry = tk.Entry(password_frame, width=20, show="*")
        self.extract_password_entry.pack(side="left")
        self.password_visible = False  # 跟踪密码是否可见
        self.toggle_password_button = tk.Button(password_frame, text="👁", command=self.toggle_password_visibility)
        self.toggle_password_button.pack(side="right")

        # 附件框架
        file_frame = tk.LabelFrame(self.send_frame, padx=10, pady=10)
        file_frame.pack(fill="x", padx=10, pady=(0,10))

        # 附件选择按钮
        tk.Button(file_frame, text="选择附件", command=self.select_files).pack(side="top", expand=True, fill="x", pady=(0,10))
        
        # 文件信息展示框
        file_list_frame = tk.Frame(file_frame)
        file_list_frame.pack(fill="both", expand=True, pady=(0,5))
        
        # 创建滚动条
        scrollbar = tk.Scrollbar(file_list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 创建文本框用于显示文件信息
        self.file_info_text = tk.Text(file_list_frame, height=5, yscrollcommand=scrollbar.set)
        self.file_info_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.file_info_text.yview)

        # 添加拖放提示
        self.file_info_text.insert(tk.END, "提示: 可以将文件拖放到此窗口或者点击按钮选择文件")
        
        # 发送按钮
        button_frame = tk.Frame(self.send_frame)
        button_frame.pack(pady=(0,5), padx=10, fill="x")
        tk.Button(button_frame, text="发送", command=self.send_email).pack(side="left", expand=True, fill="x")
        
        # 在发送邮件标签页创建时注册拖放事件
        self.send_frame.drop_target_register(DND_FILES)
        self.send_frame.dnd_bind('<<Drop>>', self.drop_files)
    def create_settings_tab(self):
        """创建设置标签页"""
        self.settings_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.settings_frame, text="设置")
        
        # SMTP设置框架
        smtp_frame = tk.LabelFrame(self.settings_frame, padx=10, pady=10)
        smtp_frame.pack(fill="x", padx=10, pady=10)
        
        # SMTP服务器
        server_frame = tk.Frame(smtp_frame)
        server_frame.pack(fill="x", pady=5)
        tk.Label(server_frame, text="SMTP服务器:", width=11, anchor="w").pack(side="left")
        self.smtp_server_entry = tk.Entry(server_frame)
        self.smtp_server_entry.pack(side="left", expand=True, fill="x")
        self.smtp_server_entry.insert(0, "smtp.example.com")
        
        # SMTP端口
        port_frame = tk.Frame(smtp_frame)
        port_frame.pack(fill="x", pady=(0,5))
        tk.Label(port_frame, text="端口:", width=11, anchor="w").pack(side="left")
        self.smtp_port_entry = tk.Entry(port_frame)
        self.smtp_port_entry.pack(side="left",expand=True, fill="x")
        self.smtp_port_entry.insert(0, "587")
        
        # 发送邮箱
        sender_frame = tk.Frame(smtp_frame)
        sender_frame.pack(fill="x", pady=(0,5))
        tk.Label(sender_frame, text="发送邮箱:", width=11, anchor="w").pack(side="left")
        self.sender_email_entry = tk.Entry(sender_frame)
        self.sender_email_entry.pack(side="left", expand=True, fill="x")
        
        # 密码
        password_frame = tk.Frame(smtp_frame)
        password_frame.pack(fill="x", pady=(0,5))
        tk.Label(password_frame, text="密码/授权码:", width=11, anchor="w").pack(side="left")
        self.password_entry = tk.Entry(password_frame, width=18, show="*")
        self.password_entry.pack(side="left")
        self.smtp_password_visible = False  # 跟踪SMTP密码是否可见
        self.toggle_smtp_password_button = tk.Button(password_frame, text="👁", command=self.toggle_smtp_password_visibility)
        self.toggle_smtp_password_button.pack(side="right")
        
        
        # 保存按钮
        settings_button_frame = tk.Frame(self.settings_frame)
        settings_button_frame.pack(pady=(0,5), padx=10, fill="x")
        tk.Button(settings_button_frame, text="保存设置", command=self.save_settings).pack(side="left", expand=True, fill="x")

    def create_status_bar(self):
        """状态栏"""
        self.status_bar = tk.Frame(self.root, relief=tk.SUNKEN, bd=1)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
        self.status_label = tk.Label(self.status_bar, text="就绪", anchor=tk.W)
        self.status_label.pack(side=tk.LEFT)

    def center_window(self):
        self.root.update_idletasks()
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        window_width = self.root.winfo_width()
        window_height = self.root.winfo_height()
        x = (screen_width - window_width) // 2
        y = (screen_height - window_height) // 2
        self.root.geometry(f"{window_width}x{window_height}+{x}+{y}")

    def toggle_password_visibility(self):
        """切换密码可见性"""
        if self.password_visible:
            # 当前是可见的，切换为隐藏
            self.extract_password_entry.config(show="*")
            self.toggle_password_button.config(text="👁")
            self.password_visible = False
        else:
            # 当前是隐藏的，切换为可见
            self.extract_password_entry.config(show="")
            self.toggle_password_button.config(text="🔒")
            self.password_visible = True    
    
    def toggle_smtp_password_visibility(self):
        """切换SMTP密码可见性"""
        if self.smtp_password_visible:
            # 当前是可见的，切换为隐藏
            self.password_entry.config(show="*")
            self.toggle_smtp_password_button.config(text="👁")
            self.smtp_password_visible = False
        else:
            # 当前是隐藏的，切换为可见
            self.password_entry.config(show="")
            self.toggle_smtp_password_button.config(text="🔒")
            self.smtp_password_visible = True
    
    def drop_files(self, event):
        # 获取拖放的文件路径
        files = self.root.tk.splitlist(event.data)
        
        if files:
            file_path = files[0]
            self.selected_files = [file_path]
            self.update_file_info_display()
            self.status_label.config(text="已通过拖放选择文件")

    def select_files(self):
        # 打开文件选择对话框，只允许选择单个文件
        file_path = filedialog.askopenfilename(
            title="选择附件",
            filetypes=[
                ("所有文件", "*.*"),
                ("文本文件", "*.txt"),
                ("PDF文件", "*.pdf"),
                ("Word文档", "*.docx"),
                ("Excel文件", "*.xlsx")
            ]
        )
        
        # 清空之前的选择
        self.selected_files = [file_path] if file_path else []
        
        # 更新显示
        self.update_file_info_display()
    
    def update_file_info_display(self):
        # 清空文本框
        self.file_info_text.delete(1.0, tk.END)
        
        # 如果没有选择文件
        if not self.selected_files or not self.selected_files[0]:
            self.file_info_text.insert(tk.END, "文件名: \n文件大小: \n路径: \n")
            return
        
        # 显示选中文件的信息
        file_path = self.selected_files[0]
        file_name = os.path.basename(file_path)
        try:
            file_size = os.path.getsize(file_path)
            file_size_str = self.format_file_size(file_size)
        except Exception as e:
            file_size_str = "无法获取大小"
        
        self.file_info_text.insert(tk.END, f"文件名: {file_name}\n")
        self.file_info_text.insert(tk.END, f"文件大小: {file_size_str}\n")
        self.file_info_text.insert(tk.END, f"路径: {file_path}\n")
    
    def format_file_size(self, size_bytes):
        """格式化文件大小显示"""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024**2:
            return f"{size_bytes/1024:.1f} KB"
        elif size_bytes < 1024**3:
            return f"{size_bytes/(1024**2):.1f} MB"
        else:
            return f"{size_bytes/(1024**3):.1f} GB"

    def save_settings(self):
        """保存设置的函数"""
        smtp_server = self.smtp_server_entry.get()
        smtp_port = self.smtp_port_entry.get()
        sender_email = self.sender_email_entry.get()
        password = self.password_entry.get()
        receiver_email = self.email_entry.get()
        volume_size = self.volume_size_entry.get()
        extract_password = self.extract_password_entry.get()
        
        # 创建配置字典
        config = {
            "smtp_server": smtp_server,
            "smtp_port": smtp_port,
            "sender_email": sender_email,
            "password": password,
            "receiver_email": receiver_email,
            "volume_size": volume_size,
            "extract_password": extract_password
        }
        
        # 保存到JSON文件
        try:
            with open("config.json", "w", encoding="utf-8") as f:
                json.dump(config, f, ensure_ascii=False, indent=4)
            # 更新状态栏
            self.status_label.config(text="设置已保存")
        except Exception as e:
            self.status_label.config(text="保存失败")
            print(f"保存设置时出错: {e}")
    
    def load_settings(self):
        """加载已保存的设置"""
        try:
            with open("config.json", "r", encoding="utf-8") as f:
                config = json.load(f)
            
            # 填充设置到界面
            self.smtp_server_entry.delete(0, tk.END)
            self.smtp_server_entry.insert(0, config.get("smtp_server", ""))
            
            self.smtp_port_entry.delete(0, tk.END)
            self.smtp_port_entry.insert(0, config.get("smtp_port", ""))
            
            self.sender_email_entry.delete(0, tk.END)
            self.sender_email_entry.insert(0, config.get("sender_email", ""))
            
            # 填充邮件发送相关设置
            self.email_entry.delete(0, tk.END)
            self.email_entry.insert(0, config.get("receiver_email", ""))
            
            self.volume_size_entry.delete(0, tk.END)
            self.volume_size_entry.insert(0, config.get("volume_size", "20"))
                       
            self.extract_password_entry.delete(0, tk.END)
            self.extract_password_entry.insert(0, config.get("extract_password", ""))
            
            # 密码字段
            self.password_entry.delete(0, tk.END)
            self.password_entry.insert(0, config.get("password", ""))
            
            self.status_label.config(text="设置已加载")
        except FileNotFoundError:
            # 配置文件不存在，使用默认值
            self.status_label.config(text="使用默认设置")
        except Exception as e:
            self.status_label.config(text="加载设置失败")
            print(f"加载设置时出错: {e}")

    def send_email(self):
        """发送邮件的函数"""
        # 保存当前配置
        self.save_settings()

        # 获取用户输入
        receiver_email = self.email_entry.get()
        volume_size = self.volume_size_entry.get()
        extract_password = self.extract_password_entry.get()
        
        # 检查是否选择了文件
        if not self.selected_files or not self.selected_files[0]:
            messagebox.showerror("错误", "请选择要发送的文件")
            return
            
        # 检查必要字段
        if not receiver_email:
            messagebox.showerror("错误", "请输入接收邮箱")
            return
            
        try:
            volume_size_mb = int(volume_size)
            if volume_size_mb <= 0:
                raise ValueError("分卷大小必须大于0")
        except ValueError as e:
            messagebox.showerror("错误", "分卷大小必须是正整数")
            return
        
        # 在新线程中执行发送操作，避免阻塞GUI
        send_thread = threading.Thread(target=self._send_email_thread, 
                                      args=(receiver_email, volume_size_mb, extract_password))
        send_thread.daemon = True
        send_thread.start()
    
    def _send_email_thread(self, receiver_email, volume_size_mb, extract_password):
        """在后台线程中发送邮件"""
        try:
            # 检查是否有选中的文件
            if not self.selected_files or not self.selected_files[0]:
                raise Exception("没有选中的文件")
            
            # 获取选中的文件
            file_path = self.selected_files[0]
            
            # 显示处理状态
            self.status_label.config(text=f"正在处理文件: {os.path.basename(file_path)}")
            self.root.update()
            
            # 分卷压缩文件
            volume_paths = self.split_and_compress_file(file_path, volume_size_mb, extract_password)
            
            # 发送邮件
            self.status_label.config(text=f"共有{len(volume_paths)}个分卷待发送")
            self.root.update()
            
            for i, volume_path in enumerate(volume_paths):
                self.status_label.config(text=f"正在发送第{i+1}个分卷...")
                self.root.update()
                
                # 发送单个分卷
                self.send_single_email(receiver_email, volume_path, i+1, len(volume_paths), extract_password)
                
                # 如果不是最后一个分卷，则等待随机1-5秒
                if i < len(volume_paths) - 1:
                    import random
                    random_interval = random.randint(1, 5)
                    self.status_label.config(text=f"等待{random_interval}秒后发送下一个分卷...")
                    self.root.update()
                    time.sleep(random_interval)
            
            # 如果有密码，在所有分卷发送完成后再发送密码邮件
            if extract_password:
                self.status_label.config(text="正在发送解压密码...")
                self.root.update()
                self.send_password_email(receiver_email, extract_password)
            
            # 清理临时文件
            for volume_path in volume_paths:
                if os.path.exists(volume_path):
                    os.remove(volume_path)
            
            self.status_label.config(text="发送完成")
            self.root.after(0, lambda: messagebox.showinfo("成功", "所有分卷已发送完成"))
            
        except Exception as e:
            self.status_label.config(text="发送失败")
            error_msg = str(e)
            self.root.after(0, lambda: messagebox.showerror("发送失败", f"发送过程中出现错误:\n{error_msg}"))
            print(f"发送邮件时出错: {e}")
    
    def split_and_compress_file(self, file_path, volume_size_mb, password=None):
        """分卷压缩文件"""
        volume_size_bytes = volume_size_mb * 1024 * 1024  # 转换为字节
        file_size = os.path.getsize(file_path)
        
        # 如果文件大小小于分卷大小，则直接压缩整个文件
        if file_size <= volume_size_bytes:
            return [self.compress_single_file(file_path, password)]
        
        # 否则使用7-Zip进行分卷压缩
        return self.split_and_compress_with_7zip(file_path, volume_size_mb, password)
        
    def compress_single_file(self, file_path, password=None):
        """使用7-Zip压缩单个文件"""
        file_name = os.path.basename(file_path)
        temp_dir = tempfile.gettempdir()
        archive_filename = file_name + ".7z"
        archive_path = os.path.join(temp_dir, archive_filename)
        
        # 使用py7zr创建加密压缩文件
        with py7zr.SevenZipFile(archive_path, 'w', password=password) as archive:
            archive.write(file_path, file_name)
        
        return archive_path
    
    def split_and_compress_with_7zip(self, file_path, volume_size_mb, password=None):
        """使用7-Zip进行分卷压缩"""
        file_name = os.path.basename(file_path)
        temp_dir = tempfile.gettempdir()
        base_name, ext = os.path.splitext(file_name)
        archive_filename = f"{base_name}.7z"
        archive_path = os.path.join(temp_dir, archive_filename)
        volume_size_bytes = volume_size_mb * 1024 * 1024  # 转换为字节
        
        # 使用py7zr和multivolumefile创建分卷压缩文件
        try:
            with multivolumefile.open(archive_path, mode='wb', volume=volume_size_bytes) as target_archive:
                with py7zr.SevenZipFile(target_archive, 'w', password=password) as archive:
                    archive.write(file_path, file_name)
        except Exception as e:
            raise Exception(f"创建分卷压缩文件时出错: {str(e)}")
        
        # 获取生成的分卷文件列表
        volumes = []
        
        # 检查所有可能的分卷文件格式
        # 首先检查 4 位数字格式 (.0001, .0002, ...)
        part_num = 1
        while True:
            volume_path = os.path.join(temp_dir, f"{archive_filename}.{part_num:04d}")
            if os.path.exists(volume_path):
                volumes.append(volume_path)
                part_num += 1
            else:
                break
        
        # 如果没有找到 4 位数字格式，尝试 3 位数字格式 (.001, .002, ...)
        if not volumes:
            part_num = 1
            while True:
                volume_path = os.path.join(temp_dir, f"{archive_filename}.{part_num:03d}")
                if os.path.exists(volume_path):
                    volumes.append(volume_path)
                    part_num += 1
                else:
                    break
        
        # 如果仍然没有找到分卷文件，检查是否只有一个未分割的文件
        if not volumes and os.path.exists(archive_path):
            volumes.append(archive_path)
            
        if not volumes:
            # 列出临时目录中的所有相关文件以帮助调试
            temp_files = []
            for f in os.listdir(temp_dir):
                if base_name in f:
                    temp_files.append(f)
            
            raise Exception(f"未能生成分卷压缩文件。临时目录中相关文件: {temp_files}")
            
        return volumes
    
    def send_single_email(self, receiver_email, attachment_path, part_num, total_parts, password=None):
        """发送单个邮件"""
        # 加载SMTP设置
        try:
            with open("config.json", "r", encoding="utf-8") as f:
                config = json.load(f)
        except Exception as e:
            raise Exception(f"无法加载配置文件: {e}")
        
        smtp_server = config.get("smtp_server")
        smtp_port = int(config.get("smtp_port", 587))
        sender_email = config.get("sender_email")
        sender_password = config.get("password")
        
        if not all([smtp_server, smtp_port, sender_email, sender_password]):
            raise Exception("SMTP设置不完整，请检查设置")
        
        # 创建邮件
        msg = MIMEMultipart()
        msg['From'] = sender_email
        msg['To'] = receiver_email
        msg['Subject'] = f"文件分卷 {part_num}/{total_parts}"
        
        # 添加邮件正文
        body = f"这是文件的第 {part_num} 个分卷，共 {total_parts} 个分卷。"
        msg.attach(MIMEText(body, 'plain', 'utf-8'))
        
        # 添加附件
        with open(attachment_path, "rb") as attachment:
            part = MIMEBase('application', 'octet-stream')
            part.set_payload(attachment.read())
        
        encoders.encode_base64(part)
        part.add_header(
            'Content-Disposition',
            f'attachment; filename= {os.path.basename(attachment_path)}'
        )
        msg.attach(part)
        
        # 发送邮件
        try:
            server = smtplib.SMTP(smtp_server, smtp_port)
            server.starttls()  # 启用TLS加密
            server.login(sender_email, sender_password)
            server.sendmail(sender_email, receiver_email, msg.as_string())
            server.quit()
        except Exception as e:
            raise Exception(f"发送邮件失败: {e}")
    
    def send_password_email(self, receiver_email, password):
        """发送包含解压密码的邮件"""
        # 加载SMTP设置
        try:
            with open("config.json", "r", encoding="utf-8") as f:
                config = json.load(f)
        except Exception as e:
            raise Exception(f"无法加载配置文件: {e}")
        
        smtp_server = config.get("smtp_server")
        smtp_port = int(config.get("smtp_port", 587))
        sender_email = config.get("sender_email")
        sender_password = config.get("password")
        
        if not all([smtp_server, smtp_port, sender_email, sender_password]):
            raise Exception("SMTP设置不完整，请检查设置")
        
        # 创建邮件
        msg = MIMEMultipart()
        msg['From'] = sender_email
        msg['To'] = receiver_email
        msg['Subject'] = "文件解压密码"
        
        # 添加邮件正文
        body = f"您收到的文件分卷的解压密码是: {password}\n\n请妥善保管此密码，仅用于解压文件。"
        msg.attach(MIMEText(body, 'plain', 'utf-8'))
        
        # 发送邮件
        try:
            server = smtplib.SMTP(smtp_server, smtp_port)
            server.starttls()  # 启用TLS加密
            server.login(sender_email, sender_password)
            server.sendmail(sender_email, receiver_email, msg.as_string())
            server.quit()
        except Exception as e:
            raise Exception(f"发送密码邮件失败: {e}")
        
if __name__ == "__main__":
    app = sendtoABCmail()
    app.root.mainloop()